﻿#include "FITKAcousticsVTKGraphObjectSEAJunctionPoint.h"
#include "FITKAcousticsVTKGraphActor.h"

// VTK
#include <vtkProperty.h>
#include <vtkUnstructuredGrid.h>
#include <vtkPoints.h>
#include <vtkGlyph3D.h>
#include <vtkSphereSource.h>

// App
#include "FITK_Kernel/FITKAppFramework/FITKGlobalData.h"
#include "FITK_Kernel/FITKAppFramework/FITKAppFramework.h"

// Data
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsSEAModel.h"
#include "FITK_Component/FITKAcousticsModel/FITKAcousticsDiscNodeList.h"
#include "FITK_Component/FITKAcousticsModel/FITKSEAJunctionPoint.h"

// Graph and filter
#include "FITKAcousticsVTKCommons.h"
#include "FITKAcousticsVTKSharedDataManager.h"
#include "FITKAcousticsVTKGraphObjectSelect.h"
#include "FITK_Interface/FITKVTKAlgorithm/FITKGraphActor.h"

namespace Exchange
{
    FITKAcousticsVTKGraphObjectSEAJunctionPoint::FITKAcousticsVTKGraphObjectSEAJunctionPoint(Acoustics::FITKSEAJunctionPoint* jPoint)
        : FITKAcousticsVTKGraphObjectSEAJunction(jPoint)
    {
        // Return if the input is nullptr.
        if (!jPoint)
        {
            return;
        }

        // Intiailize the polygon offset.
        m_polygonOffset = FITKAcousticsVTKCommons::s_polygonOffsetSEAJunction;

        // Initialize the vtk object.
        init();

        // Generate the data set.
        if (!generateDataSet(jPoint))
        {
            return;
        }

        // Generate the nodes actor.
        generateGraph();

        // Update the visibility.
        updateVisibility();

        // Save the data type.
        m_graphInfo.Type = FITKAcousticsVTKCommons::JunctionPoint;
    }

    FITKAcousticsVTKGraphObjectSEAJunctionPoint::~FITKAcousticsVTKGraphObjectSEAJunctionPoint()
    {
        // Delete the source and glyph.
        deleteVtkObj(m_sphereSource);
        deleteVtkObj(m_glyph);

        // Delete junction line grid.
        deleteVtkObj(m_pointsLine);
        deleteVtkObj(m_ugridLine);
    }

    void FITKAcousticsVTKGraphObjectSEAJunctionPoint::updateVisibility()
    {
        // Get the runtime settings for visibility.
        bool showJunc = getGlobalSEAJunctionsVisibility();
        bool showJPoints = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_JUNCTION_POINTS, true);
        bool showSEAJPoints = getRuntimeSettingAs<bool>(RTS_KEY_VISIBLE_SEA_JUNC_POINTS, true);
        bool mainFlag = FITKAcousticsVTKGraphObject3D::getDataVisibility() && showJunc && showJPoints && showSEAJPoints;

        // Update the visibility with the flags.
        //@{
        // Show or hide actors.
        setVisible(mainFlag);
        //@}
    }

    void FITKAcousticsVTKGraphObjectSEAJunctionPoint::init()
    {
        this->Superclass::init();

        // Initialize the glyph size.
        m_glyphSize = 30.;

        // Create the source and glyph.
        //@{
        m_sphereSource = vtkSphereSource::New();
        m_sphereSource->SetCenter(0., 0., 0.);
        m_sphereSource->SetRadius(m_glyphSize * 0.5);
        m_sphereSource->SetThetaResolution(10);
        m_sphereSource->SetPhiResolution(10);

        m_glyph = vtkGlyph3D::New();
        m_glyph->SetSourceConnection(m_sphereSource->GetOutputPort());
        m_glyph->SetInputData(m_ugrid);
        //@}

        // Create the junction lines grid.
        m_ugridLine = vtkUnstructuredGrid::New();
        m_pointsLine = vtkPoints::New();
        m_ugridLine->SetPoints(m_pointsLine);
    }

    bool FITKAcousticsVTKGraphObjectSEAJunctionPoint::generateDataSet(Acoustics::FITKAbstractJunction* junction)
    {
        if (!m_points || !m_ugrid || !m_pointsLine || !m_ugridLine)
        {
            return false;
        }

        Acoustics::FITKSEAJunctionPoint* jPoint = dynamic_cast<Acoustics::FITKSEAJunctionPoint*>(junction);
        if (!jPoint)
        {
            return false;
        }

        // Get the SEA node list.
        Acoustics::FITKAcousticsDiscNodeList* seaNodes = getSEANodesData();
        if (!seaNodes)
        {
            return false;
        }

        // Get the generated SEA node id.
        int centerNodeId = jPoint->getPointID();
        Core::FITKNode* node = seaNodes->getNodeByID(centerNodeId);
        if (!node)
        {
            return false;
        }

        double pos3[3]{ 0., 0., 0. };
        node->getCoor(pos3);

        // Generate the data set.
        m_points->InsertNextPoint(pos3);

        vtkSmartPointer<vtkIdList> cell = vtkSmartPointer<vtkIdList>::New();
        cell->InsertNextId(0);
        m_ugrid->InsertNextCell(VTKCellType::VTK_VERTEX, cell);

        // Update the glyph size.
        updateGlyphSizeByRatio();

        // Insert the first main node.
        m_pointsLine->InsertNextPoint(pos3);

        // Get all connections.
        QList<int> subNodeIds;
        QList<Acoustics::FITKSEAJunPointConStr> conns = jPoint->getConnections();
        for (const Acoustics::FITKSEAJunPointConStr & conn : conns)
        {
            int id = conn._pointID;
            if (subNodeIds.contains(id))
            {
                continue;
            }

            // Get the node position.
            Core::FITKNode* subNode = seaNodes->getNodeByID(id);
            if (!subNode)
            {
                continue;
            }

            double subPos3[3]{ 0., 0., 0. };
            subNode->getCoor(subPos3);

            m_pointsLine->InsertNextPoint(subPos3);

            // Save the id.
            subNodeIds.push_back(id);
        }

        // Initialize lines cells.
        for (int i = 1; i <= subNodeIds.count(); i++)
        {
            vtkSmartPointer<vtkIdList> cellLine = vtkSmartPointer<vtkIdList>::New();
            cellLine->InsertNextId(0);
            cellLine->InsertNextId(i);
            m_ugridLine->InsertNextCell(VTKCellType::VTK_LINE, cellLine);
        }

        return true;
    }

    void FITKAcousticsVTKGraphObjectSEAJunctionPoint::generateGraph()
    {
        // Initialize color.
        Acoustics::FITKSEAAbstractJunction* junction = dynamic_cast<Acoustics::FITKSEAAbstractJunction*>(_dataObj);
        if (!junction)
        {
            return;
        }

        // Create glyph actor.
        m_fActor = FITKAcousticsVTKGraphActor::New();
        m_fActor->setGraphObject(this);
        m_fActor->setActorType(ActorType::VertexActor);
        m_fActor->SetPickable(false);
        m_fActor->SetVisibility(true);
        m_fActor->GetProperty()->SetRepresentation(2);
        m_fActor->GetProperty()->SetPointSize(0.);
        m_fActor->GetProperty()->SetLineWidth(1.);
        m_fActor->GetProperty()->SetEdgeVisibility(false);
        m_fActor->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActor);

        // Set the input connection.
        m_fActor->setInputConnection(m_glyph->GetOutputPort());

        // Create lines actor.
        m_fActorLine = FITKAcousticsVTKGraphActor::New();
        m_fActorLine->setGraphObject(this);
        m_fActorLine->setActorType(ActorType::EdgeActor);
        m_fActorLine->SetPickable(false);
        m_fActorLine->SetVisibility(true);
        m_fActorLine->GetProperty()->SetRepresentation(1);
        m_fActorLine->GetProperty()->SetPointSize(0.);
        m_fActorLine->GetProperty()->SetLineWidth(2.);
        m_fActorLine->GetProperty()->SetEdgeVisibility(true);
        m_fActorLine->setRelativeCoincidentTopologyPolygonOffsetParameters(m_polygonOffset);
        addActor(m_fActorLine);

        // Set the input data.
        m_fActorLine->setInputDataObject(m_ugridLine);   

        // Update color by enable flag.
        updateColorByEnableFlag();
    }

    void FITKAcousticsVTKGraphObjectSEAJunctionPoint::updateGlyphSizeByRatio()
    {
        if (!m_sphereSource || !m_glyph)
        {
            return;
        }

        // Calculate the glyph size.
        m_glyphSize = FITKAcousticsVTKSharedDataManager::GetInstance()->getNodeWorldSizeByRatio(1.8);
        m_sphereSource->SetRadius(m_glyphSize * 0.5);

        // Update the pipeline.
        m_glyph->Update();
    }

    void FITKAcousticsVTKGraphObjectSEAJunctionPoint::resetVtkData()
    {
        // Reset the glyph grid.
        this->Superclass::resetVtkData();

        // Reset the lines grid.
        resetVtkObj(m_pointsLine);
        resetVtkObj(m_ugridLine);
    }
}   // namespace Exchange